package main.userInterface.critterui.critterviewer;

import main.model.critters.genetics.Morphology;

import java.awt.*;

/**
 * Each body part has an external parent point, and an internal child point.
 *
 * User: gabe
 * Date: 19/07/13
 */
public class BodyParts {

    private Morphology morphology = null;


    public BodyParts (Morphology morphology) {
        this.morphology = morphology;
        generateAllParts();
    }


    /**
     * Converts an abstract set of points to absolute points.
     * @param parentLinkPoint
     * @param childLinkPoint
     * @param part
     * @param subParents
     * @return
     */
    private static double[][] convertToAbsolutePoint(Point parentLinkPoint , Point childLinkPoint ,
                                                     double[][] part , Point... subParents){

        double offsetX = parentLinkPoint.getX() - childLinkPoint.getX();
        double offsetY = parentLinkPoint.getY() - childLinkPoint.getY();

        for(Point subParent : subParents){
            subParent.setLocation(subParent.getX()+offsetX , subParent.getY()+offsetY);
        }

        for(int a = 0 ; a < part.length ; a++){
            part[a][0] = part[a][0] + offsetX; part[a][1] = part[a][1] + offsetY;
            if(a != 0){
                part[a][2] = part[a][2] + offsetX; part[a][3] = part[a][3] + offsetY;
            }
        }
        return part;
    }


    private void generateAllParts(){
        buildHead();
        buildFarEar();
        buildNearEar();
        buildEye();
        buildJaw();
        buildTusk();
        buildHorn();

        buildNeck();

        buildBody();
        buildFrontNearLeg();
        buildFrontFarLeg();
        buildBackNearLeg();
        buildBackFarLeg();
        buildTail();
        //buildTailSpikes();
        //buildBackPlates();

    }



    // == HEAD ============================================================================
    private Point parent_START_head = new Point(80 , 80);
    private Point child_head_START = new Point(60 , 33);
    private double[][] head = null;
    public double[][] getHead(){
        if (head == null)
            generateAllParts();
        return head;
    }
    public void buildHead(){
        //MINX = 20.0 , MINY = 47.0 , MAXX = 123.0 , MAXY = 113.0
        // TUSK:
        // EYE: 75,70, = 55,23
        // JAW: 75,95 = 55,48
        // FAR EAR: 85,60 = 65,13
        // NEAR EAR : 95,60 = 75,13
        double[][] head = {{ 95.0 , 45.0 },
                { 103.0 , 25.0 , 85.0 , 8.0 },
                { 67.0 , 0.0 , 43.0 , 11.0 },
                { 34.0 , 20.0 , 31.0 , 33.0 },
                { 24.0 , 42.0 , 6.0 , 47.0 },
                { 0.0 , 51.0 , 7.0 , 59.0 },
                { 15.0 , 62.0 , 22.0 , 66.0 },
                { 38.0 , 57.0 , 51.0 , 53.0 }};
        this.head = convertToAbsolutePoint(parent_START_head , child_head_START , head,
                parent_head_eye, parent_head_farEar , parent_head_horn, parent_head_jaw,
                parent_head_nearEar , parent_head_neck , parent_head_tusk );
    }

    private Point parent_head_eye = new Point(55, 23);
    private Point child_eye_head = new Point(10 , 5);
    private double[][] eye = null;
    public double[][] getEye(){
        if (eye == null)
            generateAllParts();
        return eye;
    }
    public void buildEye(){
        // MINX = 65.0 , MINY = 65.0 , MAXX = 80.0 , MAXY = 74.0
        //75,70
        double mod = 1;
        if(morphology != null)
            mod = morphology.getSensory()/40;
        double[][] eye = {{ 2.0 , 0.0 },
                { 9.0 , 0.0 , 15.0 , 3.0 },
                { 11.0*mod , 9.0*mod , 0.0*mod , 7.0*mod },
                { 1.0 , 6.0 , 2.0, 0.0 }};
        this.eye = convertToAbsolutePoint(parent_head_eye , child_eye_head , eye);
    }

    private Point parent_head_tusk = new Point(0,0);
    private Point child_tusk_head = new Point(0,0);
    private double[][] tusk = null;
    public double[][] getTusk(){
        if (tusk == null)
            generateAllParts();
        return tusk;
    }
    public void buildTusk(){
        //MINX = 39.0 , MINY = 102.0 , MAXX = 55.0 , MAXY = 108.0

        double length = 1;
        if(morphology != null){
            length = ((double)morphology.getSpeed()) / 100;
        }
        double [][] tusk = {{ 8.0*length , 11.0*length  },
                { 1.0*length  , 6.0*length  , 0.0*length  , 0.0*length  },
                { 6.0*length  , 6.0*length  , 16.0*length  , 6.0*length  }};
        this.tusk = convertToAbsolutePoint(parent_head_tusk , child_tusk_head , tusk);
    }

    private Point parent_head_jaw = new Point(55,48);
    private Point child_jaw_head = new Point(38 , 8);
    private double[][] jaw = null;
    public double[][] getJaw(){
        if (jaw == null)
            generateAllParts();
        return jaw;
    }
    public void buildJaw(){
        //MINX = 37.0 , MINY = 87.0 , MAXX = 96.0 , MAXY = 123.0
        //75 95
        double [][] jaw = {{ 33.0 , 11.0 },
                { 21.0 , 16.0 , 11.0 , 19.0 },
                { 7.0 , 22.0 , 3.0 , 25.0 },
                { 0.0 , 30.0 , 11.0 , 36.0 },
                { 20.0 , 31.0 , 29.0 , 24.0 },
                { 40.0 , 24.0 , 52.0 , 16.0 },
                { 59.0 , 7.0 , 59.0 , 0.0 }};
        this.jaw = convertToAbsolutePoint(parent_head_jaw, child_jaw_head , jaw);
    }

    private Point parent_head_horn = new Point(0 , 0);
    private Point child_horn_head = new Point(0 , 0);
    private double[][] horn = null;
    public double[][] getHorn(){
        if (horn == null)
            generateAllParts();
        return horn;
    }
    private void buildHorn(){
        //MINX = 24.0 , MINY = 50.0 , MAXX = 47.0 , MAXY = 94.0
        double[][] horn = {{ 9.0 , 44.0 },
                { 3.0 , 37.0 , 0.0 , 22.0 },
                { 1.0 , 8.0 , 6.0 , 0.0 },
                { 6.0 , 13.0 , 10.0 , 20.0 },
                { 17.0 , 28.0 , 23.0 , 35.0 },
                { 18.0 , 42.0 , 9.0 , 44.0 }};
        this.horn = convertToAbsolutePoint(parent_head_horn , child_horn_head , horn);
    }

    private Point parent_head_nearEar = new Point(75 , 13);
    private Point child_nearEar_head = new Point(36 , 36);
    private double[][] nearEar = null;
    public double[][] getNearEar(){
        if (nearEar == null)
            generateAllParts();
        return nearEar;
    }
    private void buildNearEar(){
        //MINX = 59.0 , MINY = 24.0 , MAXX = 112.0 , MAXY = 72.0
        //Head : 95,60
        double mod = 1.5;
        if(morphology != null){
            mod = ((double)morphology.getSensory())/75;
        }
        double[][] nearEar = {{ 24.0*mod , 35.0*mod },
                { 24.0*mod , 18.0*mod  , 36.0*mod , 0.0*mod  },
                { 42.0*mod , 14.0*mod  , 53.0*mod , 27.0*mod  },
                { 49.0*mod , 40.0*mod  , 37.0*mod , 48.0*mod  }};
        child_nearEar_head.setLocation(child_nearEar_head.getX()*mod , child_nearEar_head.getY()*mod);
        this.nearEar = convertToAbsolutePoint(parent_head_nearEar , child_nearEar_head , nearEar);
    }

    private Point parent_head_farEar = new Point(65 , 13);
    private Point child_farEar_head = new Point(26 , 40);
    private double[][] farEar = null;
    public double[][] getFarEar(){
        if (farEar == null)
            generateAllParts();
        return farEar;
    }
    public void buildFarEar(){
        //MINX = 59.0 , MINY = 20.0 , MAXX = 95.0 , MAXY = 67.0
        //85,60 = 26,
        double mod = 1.5;
        if(morphology != null){
            mod = ((double)morphology.getSensory())/75;
        }
        double[][] farEar = {{ 18.0*mod  , 39.0*mod },
                { 15.0*mod  , 27.0*mod , 14.0*mod  , 6.0*mod },
                { 19.0*mod  ,  0.0*mod , 27.0*mod  , 17.0*mod },
                { 33.0*mod  , 24.0*mod , 36.0*mod  , 33.0*mod },
                { 36.0*mod  , 43.0*mod , 26.0*mod  , 47.0*mod }};
        child_farEar_head.setLocation(child_farEar_head.getX()*mod , child_farEar_head.getY()*mod);
        this.farEar = convertToAbsolutePoint(parent_head_farEar , child_farEar_head , farEar);
    }

    // == NECK ============================================================================
    private Point parent_head_neck = new Point(80 , 43);
    private Point child_neck_head = new Point(24 , 18);
    private double[][] neck = null;
    public double[][] getNeck(){
        if (neck == null)
            generateAllParts();
        return neck;
    }
    public void buildNeck(){
        // MINX = 76.0 , MINY = 72.0 , MAXX = 178.0 , MAXY = 166.0
        // 100,90
        double[][] neck = {{ 102.0 , 44.0 },
                { 75.0 , 34.0 , 66.0 , 30.0 },
                { 55.0 , 19.0 , 40.0 , 10.0 },
                { 27.0 , 12.0 , 36.0 , 0.0 },
                { 18.0 , 0.0 , 0.0 , 16.0 },
                { 0.0 , 31.0 , 7.0 , 51.0 },
                { 17.0 , 70.0 , 24.0 , 94.0 }};
        this.neck = convertToAbsolutePoint(parent_head_neck , child_neck_head , neck,
                parent_neck_body);
    }

    // == BODY ============================================================================
    private Point parent_neck_body = new Point(100,110);
    private Point child_body_neck = new Point(0,0);
    private double[][] body = null;
    public double[][] getBody(){
        if( body == null)
            generateAllParts();
        return body;
    }
    private void buildBody(){
        // front leg	158 , 185
        // back leg	    306 , 196
        // tail 		342 , 189
        // neck body 	128 , 138

        //MINX = 100.0 , MINY = 110.0
        double[][] body = {{ 60.0 , 0.0 },
                { 123.0 , 5.0 ,     189.0 , 27.0 },
                { 230.0 , 48.0 ,    250.0 , 64.0 },
                { 258.0 , 83.0 ,    246.0 , 106.0 },
                { 224.0 , 116.0 ,   192.0 , 108.0 },
                { 174.0 , 96.0 ,    156.0 , 100.0 },
                { 129.0 , 110.0 ,   103.0 , 114.0 },
                { 78.0 , 115.0 ,    38.0 , 105.0 },
                { 12.0 , 88.0 ,     0.0 , 53.0 }};
        this.body = convertToAbsolutePoint(parent_neck_body , child_body_neck , body,
                parent_body_frontFarLeg, parent_body_frontNearLeg,
                parent_body_backFarLeg, parent_body_backNearLeg, parent_body_tail);
    }

    // == LEGS =============================================================================
    private Point parent_body_frontNearLeg = new Point(58 , 75);
    private Point child_frontNearLeg_body = new Point(36 , 24);
    private double[][] frontNearLeg = null;
    public double[][] getFrontNearLeg(){
        if( frontNearLeg == null)
            generateAllParts();
        return frontNearLeg;
    }
    private void buildFrontNearLeg(){
        double length = 1;
        if(morphology != null){
            length = ((double)morphology.getSpeed()) / 50;
        }
        double ankle = 114*length;
        double[][] frontNearLeg = {{ 0.0 , 0.0 },
                { 5.0  , 26.0 * length  , 19.0 , 47.0 * length },   { 28.0 , 62.0 * length  , 28.0 , 93.0 * length },
                { 25.0 , 114.0 * length , 18 ,  19 + ankle },       { 8.0 , 26 + ankle ,   0.0 , 33 + ankle },
                { 7.0 , 38 + ankle ,      22.0 , 37 + ankle },      { 33.0 , 32 + ankle ,  43.0 , 16 + ankle },
                { 46.0 , 114.0 * length , 47.0 , 91.0 * length },   { 53.0 , 68.0 * length  , 61.0 , 51.0 * length },
                { 67.0 , 26.0 * length  , 67.0 , 4.0  * length }};
        this.frontNearLeg = convertToAbsolutePoint(parent_body_frontNearLeg , child_frontNearLeg_body , frontNearLeg);
    }

    private Point parent_body_frontFarLeg = new Point(36 , 82);
    private Point child_frontFarLeg_body = new Point(17 , 37);
    private double[][]frontFarLeg = null;
    public double[][] getFrontFarLeg(){
        if(frontFarLeg ==null)
            generateAllParts();
        return frontNearLeg;
    }
    private void buildFrontFarLeg(){
        double length = 1;
        if(morphology != null){
            length = ((double)morphology.getSpeed()) / 50;
        }
        double ankle = (112 *length);
        double [][] frontLeg = {{ 5.0 , 32.0 * length},
                { 11.0 , 66.0  * length ,  16.0 , 83.0 * length },
                { 19.0 , 102.0 * length ,  12.0 , 4 + ankle },
                { 0.0 , 13 + ankle  ,    3.0 , 23 + ankle },
                { 11.0 , 23 + ankle ,    26.0 , 24 + ankle },
                { 36.0 , 15 + ankle ,    42.0 , -19 + ankle },
                { 45.0 , 69.0 * length ,   44.0 , 45.0 * length },
                { 45.0 , 23.0 * length ,   53.0 , 0.0 * length }};
        this.frontFarLeg = convertToAbsolutePoint(parent_body_frontFarLeg , child_frontFarLeg_body , frontLeg);
    }

    private Point parent_body_backFarLeg = new Point(0 , 0);
    private Point child_backFarLeg_body = new Point(0 , 0);
    private double[][] backFarLeg = null;
    public double[][] getBackFarLeg(){
        if(backFarLeg == null)
            generateAllParts();
        return backFarLeg;
    }
    private void buildBackFarLeg(){
        // 273 200
        double[][] backFarLeg = {{255 , 181},
                {262 , 230 , 283 , 256},
                {291 , 268 , 295 , 285},
                {286 , 289 , 261 , 295},
                {264 , 303 , 280 , 301},
                {316 , 290 , 321 , 281},
                {311 , 263 , 305 , 240},
                {305 , 203 , 296 , 172}};
        this.backFarLeg = convertToAbsolutePoint(parent_body_backFarLeg , child_backFarLeg_body , backFarLeg);
    }

    private Point parent_body_backNearLeg = new Point(0 , 0);
    private Point child_backNearLeg_body = new Point(0 , 0);
    private double[][] backNearLeg = null;
    public double[][] getBackNearLeg(){
        if (backNearLeg == null)
            generateAllParts();
        return backNearLeg;
    }
    private void buildBackNearLeg(){
        // back leg	306 , 196
        double[][] backNearLeg = {{265 , 173},
                {283 , 225 , 311 , 249},
                {327 , 262 , 348 , 269},
                {353 , 281 , 344 , 297},
                {336 , 306 , 325 , 311},
                {332 , 319 , 341 , 319},
                {353 , 319 , 357 , 312},
                {365 , 301 , 368 , 277},
                {374 , 262 , 373 , 249},
                {362 , 246 , 349 , 217},
                {339 , 201 , 318 , 177}};
        this.backNearLeg = convertToAbsolutePoint(parent_body_backNearLeg , child_backNearLeg_body , backNearLeg);
    }

    private Point parent_body_tail = new Point(0 , 0);
    private Point child_tail_body = new Point(0 , 0);
    private double[][] tail = null;
    public double[][] getTail(){
        if (tail == null)
        generateAllParts();
        return tail;
    }
    public void buildTail(){
        //MINX = 171.0 , MINY = 171.0 , MAXX = 454.0 , MAXY = 271.0

        double[][] tail = {{ 171.0 , 0.0 },
        { 188.0 , 13.0 , 196.0 , 22.0 },
        { 209.0 , 46.0 , 225.0 , 63.0 },
        { 243.0 , 71.0 , 261.0 , 69.0 },
        { 270.0 , 64.0 , 277.0 , 51.0 },
        { 283.0 , 81.0 , 267.0 , 93.0 },
        { 244.0 , 100.0 , 224.0 , 92.0 },
        { 201.0 , 86.0 , 182.0 , 64.0 },
        { 172.0 , 49.0 , 160.0 , 35.0 }};
        this.tail = convertToAbsolutePoint(parent_body_tail , child_tail_body , tail);
    }

}
